package com.example.myproject.client;

import java.util.ArrayList;
import java.util.Collections;

import com.google.gwt.user.client.Window;

public class HabitatLayout {
	
	private ModuleLocation[][] layout = new ModuleLocation[100][50];
	private int badModules = 0;
	private int totalModules = 0;
	
	public HabitatLayout() {
		for (int i = 0; i < 100; i++) {
			for (int j = 0; j < 50; j++) {
				layout[i][j] = new ModuleLocation();
			}
		}
		// Create Sandy Area
		for (int i = 40; i <= 50; i++) {
			for (int j = 40; j <= 50; j++) {
				layout[i-1][j-1].setUsable(false);
			}
		}
	}
	
	/**
	 * Adds a module type to the plan (for creating Layout plans)
	 * @param moduleImage The type of module to add.
	 * @return if the type was added successfully (false if location is already filled)
	 */
	public void addPlanType(int x, int y, String type) {
		layout[x-1][y-1].setType(type);
	}
	
	public String getPlanType(int x, int y) {		
		return layout[x-1][y-1].getType();
	}
	
	/**
	 * Adds a module to the closest plan space from given coordinates.
	 * @param module The module to add.
	 * @param x The X coordinate.
	 * @param y The Y coordinate.
	 * @return If the module was added. (false if no space is available)
	 */
	public boolean addSmartModule(Module module) {
		totalModules++;
		// if the module is in sandy/unusable area, it is considered damaged.
		if (!layout[module.getX()-1][module.getY()-1].isUsable()) {
			layout[module.getX()-1][module.getY()-1].setModule(module);
			badModules++;
			return false;
		}
		Coordinate location = findClosest(module.getType(), module.getX() - 1, module.getY() - 1);
		// Be sure to use x-1 to place module.
		// Coordinate is (0,0) coords
		if (location == null) {
			if (layout[module.getX()-1][module.getY()-1].getType() == "") {
				layout[module.getX()-1][module.getY()-1].setModule(module);
			} else {
				Coordinate moveTo = findClosest("", module.getX() - 1, module.getY() - 1);
				layout[moveTo.getX()][moveTo.getY()].setModule(module);
			}
			badModules++;			
			return false;
		} else {
			layout[location.getX()][location.getY()].setModule(module);
			if (module.getType() == "Plain") {
				openAround(location);
			}
			return true;
		}
	}	
	
	public boolean addModuleList(ArrayList<Module> moduleArray) {
		layout[49][24].setOpen(true);
		boolean success = true;
		Collections.sort(moduleArray);		
		
		for (Module mod : moduleArray) {
			if (mod.getStatus().equals("undamaged") || mod.getStatus().equals("uncertain")) {
				if (!addSmartModule(mod)) {
					success = false;
				}
			} else {
				badModules++;
			}
		}
		return success;
	}
	
	public Module getModule(int x, int y) {
		return layout[x-1][y-1].getModule();
	}
	
	private Coordinate findClosest(String type, int xBase, int yBase) {
		boolean found = false;
		boolean edge = false;
		int range = 0;
		Coordinate coord = null;
		boolean dragPlanning = false;
		if (type.equals("DragPlanning")) {
			dragPlanning = true;
		}
		while (!found && !edge) {
			edge = true;
			// steps outward, range++ each loop (range is steady in loop)
			for (int x = 0; x <= range; x++) {
				int y = range - x;
				// x = {0-99}, y = {0-49}
				if (xBase + x <= 99 && yBase + y <= 49) {
					// +/+	
					if (layout[xBase + x][yBase + y] == null) {
						Window.alert("Null Error: " + (xBase + x) + ", " + (yBase + y));
					} else {
						edge = false;
					}
					if (dragPlanning) {
						if (layout[xBase + x][yBase + y].getModule() == null) {
							coord = new Coordinate(xBase + x, yBase + y);
							found = true;
						}
					} else {			
						if (layout[xBase + x][yBase + y].getType() == type && layout[xBase + x][yBase + y].getModule() == null && layout[xBase + x][yBase + y].isOpen()) {
							coord = new Coordinate(xBase + x, yBase + y);
							found = true;
						}
					}
				}
				if (xBase + x <= 99 && yBase - y >= 0) {
					// +/-
					if (layout[xBase + x][yBase - y] == null) {
						Window.alert("Null Error: " + (xBase + x) + ", " + (yBase - y));
					} else {
						edge = false;
					}
					if (dragPlanning) {
						if (layout[xBase + x][yBase - y].getModule() == null) {
							coord = new Coordinate(xBase + x, yBase - y);
							found = true;
						}
					} else {			
						if (layout[xBase + x][yBase - y].getType() == type && layout[xBase + x][yBase - y].getModule() == null && layout[xBase + x][yBase - y].isOpen()) {
							coord = new Coordinate(xBase + x, yBase - y);
							found = true;						
						}
					}
				}
				if (xBase - x >= 0 && yBase - y >= 0) {				
					// -/-
					if (layout[xBase - x][yBase - y] == null) {
						Window.alert("Null Error: " + (xBase - x) + ", " + (yBase - y));
					} else {
						edge = false;
					}
					if (dragPlanning) {
						if (layout[xBase - x][yBase - y].getModule() == null) {
							coord = new Coordinate(xBase - x, yBase - y);
							found = true;
						}
					} else {			
						if (layout[xBase - x][yBase - y].getType() == type && layout[xBase - x][yBase - y].getModule() == null && layout[xBase - x][yBase - y].isOpen()) {
							coord = new Coordinate(xBase - x, yBase - y);
							found = true;
						}
					}
				}
				if (xBase - x >= 0 && yBase + y <= 49) {
					// -/+
					if (layout[xBase - x][yBase + y] == null) {
						Window.alert("Null Error: " + (xBase - x) + ", " + (yBase + y));
					} else {
						edge = false;
					}
					if (dragPlanning) {
						if (layout[xBase - x][yBase + y].getModule() == null) {
							coord = new Coordinate(xBase - x, yBase + y);
							found = true;
						}
					} else {			
						if (layout[xBase - x][yBase + y].getType() == type && layout[xBase - x][yBase + y].getModule() == null && layout[xBase - x][yBase + y].isOpen()) {
							coord = new Coordinate(xBase - x, yBase + y);
							found = true;	
						}
					}
				}
			}
			range++;
		}	
		return coord;
	}
	
	private void openAround(Coordinate coord) {
		if ((coord.getY() + 1) < 50) {
			layout[coord.getX()][coord.getY() + 1].setOpen(true);
		}
		if ((coord.getY() - 1) >= 0) {
			layout[coord.getX()][coord.getY() - 1].setOpen(true);
		}
		if ((coord.getX() + 1) < 100) {
			layout[coord.getX() + 1][coord.getY()].setOpen(true);
		}
		if ((coord.getX() - 1) >= 0) {
			layout[coord.getX() - 1][coord.getY()].setOpen(true);
		}
	}

	public boolean isUsable(int x, int y) {
		return layout[x-1][y-1].isUsable();
	}
	
	public void moveModule(Module mod, int startX, int startY, int toX, int toY) {
		Coordinate location = findClosest("DragPlanning", toX - 1, toY - 1);
		if (location == null) {
			Window.alert("Planning location not found!!"); // Should never happen
		}
		if (startX == (location.getX() + 1) && startY == (location.getY() + 1)) {
			Window.alert("Same location"); // Should never happen (location will not return as it has the current module in it)
			// same location, do nothing.
		} else {
			if (layout[startX-1][startY-1].getModule() == mod) {
				//boolean open = layout[startX-1][startY-1].isOpen();
				layout[startX-1][startY-1].setModule(null);// = new ModuleLocation();
				//layout[startX-1][startY-1].setOpen(open);
				layout[location.getX()][location.getY()].setModule(mod);
				if (mod.getType().equals("Plain") && layout[location.getX()][location.getY()].isOpen()) {
					openAround(location);
				}
			} else {
				Window.alert("Wrong module location error!!");
			}
		}
		TestGUI.refreshPlanDisplay();
	}
	
	public boolean isOpen(int x, int y) {
		return layout[x-1][y-1].isOpen();
	}
	
	public double getOptimizedPercent() {
		return 100 - (((badModules * 1000)/totalModules) / 10.0);
	}
}
